Skip to content

Fix crash in path completions for URLs with a query or fragment - #65342

Open
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-completion-relative-url-fragment
Open

Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-completion-relative-url-fragment

Conversation

@RyanCavanaugh

Copy link
Copy Markdown
Member

Part of #65068 (partially: the textDocument/completion panic "path must not contain a URL query or fragment" via getCompletionEntriesForRelativeModules -> getCompletionEntriesForDirectoryFragment, comment 1)

Analysis

String-literal path completions in a module specifier treat the specifier as "relative" when it starts with .//../, or (with no paths) when it is a rooted disk path or a URL (tspath.IsUrl, i.e. anything containing ://). getCompletionEntriesForDirectoryFragment then strips the basename and resolves the remaining directory portion against the script directory with RootedDirectoryPath.ResolveDirectory.

If that directory portion is URL-rooted and contains a ? or #, ResolveDirectory -> ToRootedPath hits the (intentional) assertion that rooted paths never carry a URL query or fragment:

declare function require(id: string): any;
require("https://example.com/a?b/c|");            // directory "https://example.com/a?b/"
require("http://localhost/#/routes/|");            // directory "http://localhost/#/routes/"
require("./foo https://example.com/?x/y|");        // "://" anywhere makes the whole string URL-rooted

(import/import() with such specifiers hit a separate module-resolution panic first, which is handled elsewhere; require in a .ts file isn't resolved, so it isolates the completion crash.) The same assertion is reachable when the script directory itself is URL-rooted and a relative fragment contains ?/#.

Before paths were strongly typed, this code used tspath.ResolvePath + DirectoryExists, which simply found no such directory. Strada (src/services/stringCompletions.ts) does the same: resolvePath(scriptDirectory, fragment) followed by tryDirectoryExists, which yields no completions for these inputs.

Fix

Add RootedDirectoryPath.TryResolveDirectory, which mirrors ResolveDirectory but returns false (instead of panicking) exactly when the path would resolve to a URL with a query or fragment. getCompletionEntriesForDirectoryFragment uses it and returns no path completions in that case, matching Strada. The tspath assertions are unchanged.

Because the fix is inside getCompletionEntriesForDirectoryFragment, it should also cover the sibling crash in #65068 where getCompletionEntriesForNonRelativeModules calls getCompletionEntriesForDirectoryFragment (comment 3), though that path isn't separately tested here.

Alternatives considered:

  • Checking strings.ContainsAny(fragment, "?#") in the language service: this would also wrongly drop completions for legitimate on-disk directories whose names contain ?/# on non-URL paths, and would need to duplicate tspath's URL-root detection.
  • Weakening the tspath assertion: rejected; rooted paths should never contain a URL query/fragment.

Tests: new fourslash test completionForStringLiteralUrlQueryOrFragmentNoCrash (panicked before the fix with the exact message from the issue), plus a small unit test for TryResolveDirectory.

Copilot Checklist

I successfully ran the applicable command at the end of my session, and it completed without error:

  • npx hereby validate
  • npx hereby validate --api (for TypeScript API changes)

Completions inside a module specifier such as
`require("https://example.com/a?b/c")` treat the URL-like specifier as a
relative path and resolve its directory portion against the script
directory. When that directory portion is a URL containing `?` or `#`,
ResolveDirectory panics because rooted paths never carry a URL query or
fragment.

Add RootedDirectoryPath.TryResolveDirectory and use it in
getCompletionEntriesForDirectoryFragment so that such fragments produce
no path completions, matching Strada (where the resolved directory simply
does not exist).

Part of microsoft#65068

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec07792-6b96-4d6d-b2c8-c8a49abd0ce6
Copilot AI balanced review requested due to automatic review settings November 5, 2026 18:47
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Nov 5, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Nov 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block the URL path-completion changes.

No open issues 🎉

What changed in this PR

Prevents language-server path completions from panicking when a directory resolves to a URL containing a query or fragment, partially addressing #65068 while preserving rooted-path assertions.

Changes:

  • Adds TryResolveDirectory to reject unsupported URL paths without panicking.
  • Returns no directory completions when resolution is rejected.
  • Adds unit and fourslash regression tests, including coverage that preserves valid disk paths containing ?.
File Description
tsc/​internal/​tspath/​typed_paths_test.go Tests rejected URL paths and successful directory resolution.
tsc/​internal/​tspath/​rooted_path.go Adds the non-panicking directory resolution helper.
tsc/​internal/​ls/​string_completions.go Uses the helper to safely skip unsupported directory paths.
tsc/​internal/​fourslash/​tests/​completionForStringLiteralUrlQueryOrFragmentNoCrash_test.go Covers three URL-related completion crash scenarios.

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

2 participants